@crestapps/ai-chat-ui 2.0.0-preview.215 → 2.0.0-preview.223

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/dist/ai-chat-widget.js +762 -964
  2. package/dist/ai-chat-widget.js.map +1 -1
  3. package/dist/ai-chat-widget.min.js +1 -1
  4. package/dist/ai-chat-widget.min.js.map +1 -1
  5. package/dist/ai-chat.css +0 -2
  6. package/dist/ai-chat.js +2380 -3698
  7. package/dist/ai-chat.js.map +1 -1
  8. package/dist/ai-chat.min.css +1 -1
  9. package/dist/ai-chat.min.js +78 -1
  10. package/dist/ai-chat.min.js.map +1 -1
  11. package/dist/chat-image-carousel.css +0 -2
  12. package/dist/chat-image-carousel.js +306 -482
  13. package/dist/chat-image-carousel.js.map +1 -1
  14. package/dist/chat-image-carousel.min.css +1 -1
  15. package/dist/chat-image-carousel.min.js +1 -1
  16. package/dist/chat-image-carousel.min.js.map +1 -1
  17. package/dist/chat-interaction-settings.js +221 -293
  18. package/dist/chat-interaction-settings.js.map +1 -1
  19. package/dist/chat-interaction-settings.min.js +1 -1
  20. package/dist/chat-interaction-settings.min.js.map +1 -1
  21. package/dist/chat-interaction.js +2330 -3557
  22. package/dist/chat-interaction.js.map +1 -1
  23. package/dist/chat-interaction.min.js +62 -1
  24. package/dist/chat-interaction.min.js.map +1 -1
  25. package/dist/chat-markers.js +193 -485
  26. package/dist/chat-markers.js.map +1 -1
  27. package/dist/chat-markers.min.js +2 -1
  28. package/dist/chat-markers.min.js.map +1 -1
  29. package/dist/chat-widget.css +0 -2
  30. package/dist/chat-widget.min.css +1 -1
  31. package/dist/document-drop-zone.css +0 -2
  32. package/dist/document-drop-zone.js +127 -153
  33. package/dist/document-drop-zone.js.map +1 -1
  34. package/dist/document-drop-zone.min.css +1 -1
  35. package/dist/document-drop-zone.min.js +1 -1
  36. package/dist/document-drop-zone.min.js.map +1 -1
  37. package/dist/realtime-audio.js +1639 -2504
  38. package/dist/realtime-audio.js.map +1 -1
  39. package/dist/realtime-audio.min.js +3 -1
  40. package/dist/realtime-audio.min.js.map +1 -1
  41. package/dist/technical-name-generator.js +58 -80
  42. package/dist/technical-name-generator.js.map +1 -1
  43. package/dist/technical-name-generator.min.js +1 -1
  44. package/dist/technical-name-generator.min.js.map +1 -1
  45. package/package.json +1 -5
  46. package/dist/ai-chat.css.map +0 -1
  47. package/dist/chat-image-carousel.css.map +0 -1
  48. package/dist/chat-widget.css.map +0 -1
  49. package/dist/document-drop-zone.css.map +0 -1
@@ -1,967 +1,765 @@
1
1
  /*
2
- ** NOTE: This file is generated by Gulp and should not be edited directly!
3
- ** Any changes made directly to this file will be overwritten next time its asset group is processed by Gulp.
2
+ ** NOTE: This file is generated by the assets build (npm run build) and should not be edited directly!
3
+ ** Edit its sources under Assets/ and rebuild; see Assets.json for the files it is built from.
4
4
  */
5
-
6
- function clampWidgetValue(value, min, max) {
7
- return Math.min(Math.max(value, min), max);
8
- }
9
- window.coreAIChatWidgetBehavior = window.coreAIChatWidgetBehavior || function () {
10
- function parsePixelValue(value) {
11
- if (typeof value === 'number' && Number.isFinite(value)) {
12
- return value;
13
- }
14
- if (typeof value !== 'string') {
15
- return null;
16
- }
17
- var parsed = parseFloat(value);
18
- return Number.isFinite(parsed) ? parsed : null;
19
- }
20
- function attach(app, config) {
21
- if (!app || app.__aiChatWidgetAttached) {
22
- return;
23
- }
24
- app.__aiChatWidgetAttached = true;
25
- app.chatWidgetStateName = null;
26
- app.chatWidgetStateSession = null;
27
- app.chatWidgetStateDocuments = null;
28
- app.chatWidgetLayoutKey = null;
29
- app.chatHistorySection = null;
30
- app.widgetContainerElement = null;
31
- app.widgetToggleButtonElement = null;
32
- app.widgetResetSizeButtonElement = null;
33
- app.widgetResizeObserver = null;
34
- app.widgetViewportResizeHandler = null;
35
- app.widgetDefaultWidth = null;
36
- app.widgetDefaultHeight = null;
37
- app.widgetIsInitialized = false;
38
- app.getStoredWidgetLayout = function () {
39
- if (!this.chatWidgetLayoutKey || !config.widget.persistLayout) {
40
- return {};
41
- }
42
- try {
43
- var storedLayout = localStorage.getItem(this.chatWidgetLayoutKey);
44
- return storedLayout ? JSON.parse(storedLayout) || {} : {};
45
- } catch (error) {
46
- console.warn('Failed to read widget layout state.', error);
47
- return {};
48
- }
49
- };
50
- app.saveStoredWidgetLayout = function (partialLayout) {
51
- if (!this.chatWidgetLayoutKey || !config.widget.persistLayout) {
52
- return;
53
- }
54
- var currentLayout = this.getStoredWidgetLayout();
55
- var nextLayout = Object.assign({}, currentLayout);
56
- if (partialLayout.panel !== undefined) {
57
- nextLayout.panel = Object.assign({}, currentLayout.panel || {}, partialLayout.panel || {});
58
- }
59
- if (partialLayout.position !== undefined) {
60
- nextLayout.position = Object.assign({}, currentLayout.position || {}, partialLayout.position || {});
61
- }
62
- localStorage.setItem(this.chatWidgetLayoutKey, JSON.stringify(nextLayout));
63
- };
64
- app.getElementSize = function (element, fallbackWidth, fallbackHeight) {
65
- if (!element) {
66
- return {
67
- width: fallbackWidth || 0,
68
- height: fallbackHeight || 0
69
- };
70
- }
71
- var rect = element.getBoundingClientRect();
72
- var computed = window.getComputedStyle(element);
73
- return {
74
- width: Math.round(rect.width || parsePixelValue(computed.width) || fallbackWidth || 0),
75
- height: Math.round(rect.height || parsePixelValue(computed.height) || fallbackHeight || 0)
76
- };
77
- };
78
- app.getElementPosition = function (element) {
79
- if (!element) {
80
- return {
81
- left: 0,
82
- top: 0
83
- };
84
- }
85
- var rect = element.getBoundingClientRect();
86
- var computed = window.getComputedStyle(element);
87
- var inlineLeft = parsePixelValue(element.style.left);
88
- var inlineTop = parsePixelValue(element.style.top);
89
- var computedLeft = parsePixelValue(computed.left);
90
- var computedTop = parsePixelValue(computed.top);
91
- var isHidden = computed.display === 'none';
92
- return {
93
- left: Math.round(isHidden && Number.isFinite(inlineLeft) ? inlineLeft : rect.left || inlineLeft || computedLeft || 0),
94
- top: Math.round(isHidden && Number.isFinite(inlineTop) ? inlineTop : rect.top || inlineTop || computedTop || 0)
95
- };
96
- };
97
- app.getAvailableWidgetPositionRange = function (width, height) {
98
- var viewportPadding = 8;
99
- return {
100
- padding: viewportPadding,
101
- maxLeft: Math.max(viewportPadding, window.innerWidth - width - viewportPadding),
102
- maxTop: Math.max(viewportPadding, window.innerHeight - height - viewportPadding)
103
- };
104
- };
105
- app.createStoredPosition = function (left, top, width, height) {
106
- var range = this.getAvailableWidgetPositionRange(width, height);
107
- var leftSpan = Math.max(0, range.maxLeft - range.padding);
108
- var topSpan = Math.max(0, range.maxTop - range.padding);
109
- return {
110
- left: Math.round(left),
111
- top: Math.round(top),
112
- leftRatio: leftSpan === 0 ? 0 : (left - range.padding) / leftSpan,
113
- topRatio: topSpan === 0 ? 0 : (top - range.padding) / topSpan
114
- };
115
- };
116
- app.resolveStoredPosition = function (storedPosition, width, height) {
117
- if (!storedPosition) {
118
- return null;
119
- }
120
- var range = this.getAvailableWidgetPositionRange(width, height);
121
- var left = Number(storedPosition.left);
122
- var top = Number(storedPosition.top);
123
- var leftRatio = Number(storedPosition.leftRatio);
124
- var topRatio = Number(storedPosition.topRatio);
125
- if (Number.isFinite(leftRatio)) {
126
- left = range.padding + Math.max(0, range.maxLeft - range.padding) * leftRatio;
127
- }
128
- if (Number.isFinite(topRatio)) {
129
- top = range.padding + Math.max(0, range.maxTop - range.padding) * topRatio;
130
- }
131
- if (!Number.isFinite(left) || !Number.isFinite(top)) {
132
- return null;
133
- }
134
- return this.clampWidgetPosition(left, top, width, height);
135
- };
136
- app.clampWidgetPosition = function (left, top, width, height) {
137
- var range = this.getAvailableWidgetPositionRange(width, height);
138
- return {
139
- left: Math.round(clampWidgetValue(left, range.padding, range.maxLeft)),
140
- top: Math.round(clampWidgetValue(top, range.padding, range.maxTop))
141
- };
142
- };
143
- app.getStoredWidgetDocuments = function () {
144
- this.clearStoredWidgetDocuments();
145
- return [];
146
- };
147
- app.saveStoredWidgetDocuments = function () {
148
- this.clearStoredWidgetDocuments();
149
- };
150
- app.clearStoredWidgetDocuments = function () {
151
- if (!this.chatWidgetStateDocuments) {
152
- return;
153
- }
154
- localStorage.removeItem(this.chatWidgetStateDocuments);
155
- };
156
- app.applyAbsolutePosition = function (element, left, top) {
157
- if (!element) {
158
- return;
159
- }
160
- element.style.left = left + 'px';
161
- element.style.top = top + 'px';
162
- element.style.right = 'auto';
163
- element.style.bottom = 'auto';
164
- };
165
- app.getWidgetDefaultSize = function () {
166
- return {
167
- width: this.widgetDefaultWidth || parsePixelValue(config.widget.defaultWidth) || 380,
168
- height: this.widgetDefaultHeight || parsePixelValue(config.widget.defaultHeight) || 520
169
- };
170
- };
171
- app.getWidgetToggleGap = function () {
172
- return 8;
173
- };
174
- app.isWidgetPanelVisible = function () {
175
- if (!this.widgetContainerElement) {
176
- return false;
177
- }
178
- var computed = window.getComputedStyle(this.widgetContainerElement);
179
- return this.widgetContainerElement.classList.contains('open') && computed.display !== 'none';
180
- };
181
- app.persistWidgetPanelPosition = function () {
182
- if (!this.widgetContainerElement || !config.widget.persistLayout) {
183
- return;
184
- }
185
- var panelDefaultSize = this.getWidgetDefaultSize();
186
- var panelPosition = this.getElementPosition(this.widgetContainerElement);
187
- var panelSize = this.getElementSize(this.widgetContainerElement, panelDefaultSize.width, panelDefaultSize.height);
188
- var toggleSize = this.getElementSize(this.widgetToggleButtonElement, 52, 52);
189
- var linkedPosition = this.clampWidgetPosition(panelPosition.left + panelSize.width - toggleSize.width, panelPosition.top + panelSize.height + this.getWidgetToggleGap(), toggleSize.width, toggleSize.height);
190
- this.saveStoredWidgetLayout({
191
- position: this.createStoredPosition(linkedPosition.left, linkedPosition.top, toggleSize.width, toggleSize.height)
192
- });
193
- };
194
- app.persistWidgetTogglePosition = function () {
195
- if (!this.widgetToggleButtonElement || !config.widget.persistLayout) {
196
- return;
197
- }
198
- var togglePosition = this.getElementPosition(this.widgetToggleButtonElement);
199
- var toggleSize = this.getElementSize(this.widgetToggleButtonElement, 52, 52);
200
- this.saveStoredWidgetLayout({
201
- position: this.createStoredPosition(togglePosition.left, togglePosition.top, toggleSize.width, toggleSize.height)
202
- });
203
- };
204
- app.persistWidgetPanelSize = function () {
205
- if (!this.widgetContainerElement || !config.widget.enableResizing || !config.widget.persistLayout) {
206
- return;
207
- }
208
- var defaultSize = this.getWidgetDefaultSize();
209
- var currentSize = this.getElementSize(this.widgetContainerElement, defaultSize.width, defaultSize.height);
210
- this.saveStoredWidgetLayout({
211
- panel: {
212
- width: Math.abs(currentSize.width - defaultSize.width) <= 1 ? null : currentSize.width,
213
- height: Math.abs(currentSize.height - defaultSize.height) <= 1 ? null : currentSize.height
214
- }
215
- });
216
- };
217
- app.syncTogglePositionToPanel = function (persistLayout) {
218
- if (persistLayout === undefined) {
219
- persistLayout = true;
220
- }
221
- if (!this.widgetContainerElement || !this.widgetToggleButtonElement) {
222
- return;
223
- }
224
- var panelDefaultSize = this.getWidgetDefaultSize();
225
- var panelSize = this.getElementSize(this.widgetContainerElement, panelDefaultSize.width, panelDefaultSize.height);
226
- var toggleSize = this.getElementSize(this.widgetToggleButtonElement, 52, 52);
227
- var panelPosition = this.getElementPosition(this.widgetContainerElement);
228
- var linkedPosition = this.clampWidgetPosition(panelPosition.left + panelSize.width - toggleSize.width, panelPosition.top + panelSize.height + this.getWidgetToggleGap(), toggleSize.width, toggleSize.height);
229
- this.applyAbsolutePosition(this.widgetToggleButtonElement, linkedPosition.left, linkedPosition.top);
230
- if (persistLayout && config.widget.persistLayout) {
231
- this.persistWidgetTogglePosition();
232
- }
233
- };
234
- app.syncPanelPositionToToggle = function (persistLayout) {
235
- if (persistLayout === undefined) {
236
- persistLayout = true;
237
- }
238
- if (!this.widgetContainerElement || !this.widgetToggleButtonElement) {
239
- return;
240
- }
241
- var panelDefaultSize = this.getWidgetDefaultSize();
242
- var panelSize = this.getElementSize(this.widgetContainerElement, panelDefaultSize.width, panelDefaultSize.height);
243
- var toggleSize = this.getElementSize(this.widgetToggleButtonElement, 52, 52);
244
- var togglePosition = this.getElementPosition(this.widgetToggleButtonElement);
245
- var linkedPosition = this.clampWidgetPosition(togglePosition.left + toggleSize.width - panelSize.width, togglePosition.top - panelSize.height - this.getWidgetToggleGap(), panelSize.width, panelSize.height);
246
- this.applyAbsolutePosition(this.widgetContainerElement, linkedPosition.left, linkedPosition.top);
247
- if (persistLayout && config.widget.persistLayout) {
248
- this.persistWidgetPanelPosition();
249
- }
250
- };
251
- app.updateWidgetResetSizeButtonVisibility = function () {
252
- if (!this.widgetResetSizeButtonElement) {
253
- return;
254
- }
255
- if (!config.widget.enableResizing || !this.widgetContainerElement) {
256
- this.widgetResetSizeButtonElement.classList.add('d-none');
257
- return;
258
- }
259
- var defaultSize = this.getWidgetDefaultSize();
260
- var currentSize = this.getElementSize(this.widgetContainerElement, defaultSize.width, defaultSize.height);
261
- var isResized = Math.abs(currentSize.width - defaultSize.width) > 1 || Math.abs(currentSize.height - defaultSize.height) > 1 || !!this.widgetContainerElement.style.width || !!this.widgetContainerElement.style.height;
262
- this.widgetResetSizeButtonElement.classList.toggle('d-none', !isResized);
263
- };
264
- app.resetWidgetSize = function () {
265
- if (!this.widgetContainerElement || !config.widget.enableResizing) {
266
- return;
267
- }
268
- this.widgetContainerElement.style.removeProperty('width');
269
- this.widgetContainerElement.style.removeProperty('height');
270
- this.persistWidgetPanelSize();
271
- this.constrainWidgetElementsToViewport();
272
- this.updateWidgetResetSizeButtonVisibility();
273
- };
274
- app.restoreWidgetLayout = function () {
275
- var storedLayout = this.getStoredWidgetLayout();
276
- if (this.widgetContainerElement && config.widget.enableResizing) {
277
- if (Number.isFinite(storedLayout.panel && storedLayout.panel.width)) {
278
- this.widgetContainerElement.style.width = storedLayout.panel.width + 'px';
279
- } else {
280
- this.widgetContainerElement.style.removeProperty('width');
281
- }
282
- if (Number.isFinite(storedLayout.panel && storedLayout.panel.height)) {
283
- this.widgetContainerElement.style.height = storedLayout.panel.height + 'px';
284
- } else {
285
- this.widgetContainerElement.style.removeProperty('height');
286
- }
287
- }
288
- var toggleSize = this.getElementSize(this.widgetToggleButtonElement, 52, 52);
289
- var storedTogglePosition = this.resolveStoredPosition(storedLayout.position, toggleSize.width, toggleSize.height);
290
- if (this.widgetToggleButtonElement && config.widget.enableDragging && storedTogglePosition) {
291
- this.applyAbsolutePosition(this.widgetToggleButtonElement, storedTogglePosition.left, storedTogglePosition.top);
292
- this.syncPanelPositionToToggle(false);
293
- } else if (this.widgetToggleButtonElement && config.widget.enableDragging && Number.isFinite(storedLayout.toggle && storedLayout.toggle.left) && Number.isFinite(storedLayout.toggle && storedLayout.toggle.top)) {
294
- this.applyAbsolutePosition(this.widgetToggleButtonElement, storedLayout.toggle.left, storedLayout.toggle.top);
295
- this.syncPanelPositionToToggle(false);
296
- } else if (this.widgetContainerElement && config.widget.enableDragging && Number.isFinite(storedLayout.panel && storedLayout.panel.left) && Number.isFinite(storedLayout.panel && storedLayout.panel.top)) {
297
- this.applyAbsolutePosition(this.widgetContainerElement, storedLayout.panel.left, storedLayout.panel.top);
298
- this.syncTogglePositionToPanel(false);
299
- }
300
- this.constrainWidgetElementsToViewport();
301
- this.updateWidgetResetSizeButtonVisibility();
302
- };
303
- app.attachDraggableWidgetElement = function (element, handle, layoutKey, suppressClickOnDrag) {
304
- if (!element || !handle || !config.widget.enableDragging) {
305
- return;
306
- }
307
- var activePointerId = null;
308
- var startX = 0;
309
- var startY = 0;
310
- var startLeft = 0;
311
- var startTop = 0;
312
- var dragged = false;
313
- var dragThreshold = 4;
314
- var interactiveSelector = 'button, a, input, textarea, select, option, label';
315
- var self = this;
316
- function stopDragging(event) {
317
- if (activePointerId === null) {
318
- return;
319
- }
320
- if (event && event.pointerId !== undefined && event.pointerId !== activePointerId) {
321
- return;
322
- }
323
- document.removeEventListener('pointermove', onPointerMove);
324
- document.removeEventListener('pointerup', stopDragging);
325
- document.removeEventListener('pointercancel', stopDragging);
326
- element.classList.remove('ai-chat-widget-dragging');
327
- activePointerId = null;
328
- if (!dragged) {
329
- return;
330
- }
331
- if (layoutKey === 'panel') {
332
- self.syncTogglePositionToPanel(false);
333
- self.persistWidgetPanelPosition();
334
- } else if (layoutKey === 'toggle') {
335
- self.syncPanelPositionToToggle(false);
336
- self.persistWidgetTogglePosition();
337
- }
338
- if (config.widget.persistLayout) {
339
- self.persistWidgetPanelPosition();
340
- self.persistWidgetTogglePosition();
341
- }
342
- if (suppressClickOnDrag) {
343
- element.dataset.dragSuppressClick = 'true';
344
- window.setTimeout(function () {
345
- if (element.dataset.dragSuppressClick === 'true') {
346
- element.dataset.dragSuppressClick = 'false';
347
- }
348
- }, 0);
349
- }
350
- }
351
- function onPointerMove(event) {
352
- if (activePointerId === null || event.pointerId !== activePointerId) {
353
- return;
354
- }
355
- var deltaX = event.clientX - startX;
356
- var deltaY = event.clientY - startY;
357
- if (!dragged && Math.hypot(deltaX, deltaY) < dragThreshold) {
358
- return;
359
- }
360
- dragged = true;
361
- var defaultSize = self.getWidgetDefaultSize();
362
- var currentSize = self.getElementSize(element, layoutKey === 'panel' ? defaultSize.width : 52, layoutKey === 'panel' ? defaultSize.height : 52);
363
- var nextPosition = self.clampWidgetPosition(startLeft + deltaX, startTop + deltaY, currentSize.width, currentSize.height);
364
- self.applyAbsolutePosition(element, nextPosition.left, nextPosition.top);
365
- if (layoutKey === 'panel') {
366
- self.syncTogglePositionToPanel(false);
367
- } else if (layoutKey === 'toggle') {
368
- self.syncPanelPositionToToggle(false);
369
- }
370
- }
371
- handle.addEventListener('pointerdown', function (event) {
372
- if (event.button !== 0) {
373
- return;
374
- }
375
- if (layoutKey === 'panel' && event.target.closest(interactiveSelector)) {
376
- return;
377
- }
378
- var rect = element.getBoundingClientRect();
379
- startX = event.clientX;
380
- startY = event.clientY;
381
- startLeft = rect.left;
382
- startTop = rect.top;
383
- dragged = false;
384
- activePointerId = event.pointerId;
385
- element.classList.add('ai-chat-widget-dragging');
386
- if (handle.setPointerCapture) {
387
- handle.setPointerCapture(event.pointerId);
388
- }
389
- document.addEventListener('pointermove', onPointerMove);
390
- document.addEventListener('pointerup', stopDragging);
391
- document.addEventListener('pointercancel', stopDragging);
392
- event.preventDefault();
393
- });
394
- };
395
- app.constrainWidgetElementsToViewport = function () {
396
- if (this.widgetContainerElement && this.widgetContainerElement.style.left && this.isWidgetPanelVisible()) {
397
- var panelDefaultSize = this.getWidgetDefaultSize();
398
- var panelSize = this.getElementSize(this.widgetContainerElement, panelDefaultSize.width, panelDefaultSize.height);
399
- var panelPosition = this.getElementPosition(this.widgetContainerElement);
400
- var clampedPanel = this.clampWidgetPosition(panelPosition.left, panelPosition.top, panelSize.width, panelSize.height);
401
- this.applyAbsolutePosition(this.widgetContainerElement, clampedPanel.left, clampedPanel.top);
402
- this.syncTogglePositionToPanel(false);
403
- if (config.widget.persistLayout) {
404
- this.persistWidgetPanelPosition();
405
- this.persistWidgetTogglePosition();
406
- }
407
- } else if (this.widgetToggleButtonElement && this.widgetToggleButtonElement.style.left) {
408
- var toggleSize = this.getElementSize(this.widgetToggleButtonElement, 52, 52);
409
- var togglePosition = this.getElementPosition(this.widgetToggleButtonElement);
410
- var clampedToggle = this.clampWidgetPosition(togglePosition.left, togglePosition.top, toggleSize.width, toggleSize.height);
411
- this.applyAbsolutePosition(this.widgetToggleButtonElement, clampedToggle.left, clampedToggle.top);
412
- this.syncPanelPositionToToggle(false);
413
- if (config.widget.persistLayout) {
414
- this.persistWidgetTogglePosition();
415
- this.persistWidgetPanelPosition();
416
- }
417
- }
418
- };
419
- app.showChatScreen = function () {
420
- if (!this.chatHistorySection) {
421
- return;
422
- }
423
- this.chatHistorySection.classList.remove('show');
424
- };
425
- }
426
- function initialize(app, config) {
427
- if (!app || app.widgetIsInitialized) {
428
- return;
429
- }
430
- if (!config.widget.chatWidgetContainer) {
431
- console.error('The widget chatWidgetContainer is required.');
432
- return;
433
- }
434
- if (!config.widget.chatWidgetStateName) {
435
- console.error('The widget chatWidgetStateName is required.');
436
- return;
437
- }
438
- var chatWidgetContainer = document.querySelector(config.widget.chatWidgetContainer);
439
- if (!chatWidgetContainer) {
440
- return;
441
- }
442
- if (config.widget.chatHistorySection) {
443
- app.chatHistorySection = document.querySelector(config.widget.chatHistorySection);
444
- }
445
- app.chatWidgetStateName = config.widget.chatWidgetStateName;
446
- app.chatWidgetStateSession = config.widget.chatWidgetStateName + 'Session';
447
- app.chatWidgetStateDocuments = config.widget.chatWidgetStateName + 'Documents';
448
- app.chatWidgetLayoutKey = config.widget.chatWidgetStateName + 'Layout';
449
- app.widgetContainerElement = chatWidgetContainer;
450
- app.widgetToggleButtonElement = config.widget.toggleButtonSelector ? document.querySelector(config.widget.toggleButtonSelector) : null;
451
- app.widgetResetSizeButtonElement = config.widget.resetSizeButtonSelector ? document.querySelector(config.widget.resetSizeButtonSelector) : null;
452
- app.widgetDefaultWidth = parsePixelValue(config.widget.defaultWidth) || parsePixelValue(window.getComputedStyle(chatWidgetContainer).width) || 380;
453
- app.widgetDefaultHeight = parsePixelValue(config.widget.defaultHeight) || parsePixelValue(window.getComputedStyle(chatWidgetContainer).height) || 520;
454
- app.widgetIsInitialized = true;
455
- app.clearStoredWidgetDocuments();
456
- app.documents = [];
457
- if (config.widget.enableResizing) {
458
- chatWidgetContainer.classList.add('ai-chat-widget-resizable');
459
- chatWidgetContainer.style.minWidth = (parsePixelValue(config.widget.minWidth) || 320) + 'px';
460
- chatWidgetContainer.style.minHeight = (parsePixelValue(config.widget.minHeight) || 420) + 'px';
461
- if (typeof ResizeObserver !== 'undefined') {
462
- app.widgetResizeObserver = new ResizeObserver(function () {
463
- app.persistWidgetPanelSize();
464
- app.constrainWidgetElementsToViewport();
465
- app.updateWidgetResetSizeButtonVisibility();
466
- });
467
- app.widgetResizeObserver.observe(chatWidgetContainer);
468
- }
469
- }
470
- var dragHandleSelector = config.widget.dragHandleSelector || '.ai-chat-widget-header';
471
- var dragHandle = chatWidgetContainer.querySelector(dragHandleSelector);
472
- if (config.widget.enableDragging && dragHandle) {
473
- dragHandle.classList.add('ai-chat-widget-drag-handle');
474
- chatWidgetContainer.classList.add('ai-chat-widget-draggable');
475
- app.attachDraggableWidgetElement(chatWidgetContainer, dragHandle, 'panel', false);
476
- }
477
- if (config.widget.enableDragging && app.widgetToggleButtonElement) {
478
- app.widgetToggleButtonElement.classList.add('ai-chat-widget-draggable');
479
- app.attachDraggableWidgetElement(app.widgetToggleButtonElement, app.widgetToggleButtonElement, 'toggle', true);
480
- }
481
- if (app.widgetResetSizeButtonElement) {
482
- app.widgetResetSizeButtonElement.addEventListener('click', function (event) {
483
- event.preventDefault();
484
- app.resetWidgetSize();
485
- });
486
- }
487
- app.restoreWidgetLayout();
488
- if (!app.widgetViewportResizeHandler) {
489
- app.widgetViewportResizeHandler = function () {
490
- if (config.widget.persistLayout) {
491
- app.restoreWidgetLayout();
492
- } else {
493
- app.constrainWidgetElementsToViewport();
494
- }
495
- app.updateWidgetResetSizeButtonVisibility();
496
- };
497
- window.addEventListener('resize', app.widgetViewportResizeHandler);
498
- }
499
- app.reloadCurrentSession();
500
- if (config.widget.showHistoryButton && app.chatHistorySection) {
501
- var showHistoryButton = document.querySelector(config.widget.showHistoryButton);
502
- if (showHistoryButton) {
503
- showHistoryButton.addEventListener('click', function () {
504
- app.chatHistorySection.classList.toggle('show');
505
- });
506
- }
507
- if (config.widget.closeHistoryButton) {
508
- var closeHistoryButton = document.querySelector(config.widget.closeHistoryButton);
509
- if (closeHistoryButton) {
510
- closeHistoryButton.addEventListener('click', function () {
511
- app.showChatScreen();
512
- });
513
- }
514
- }
515
- }
516
- if (config.widget.newChatButton) {
517
- var newChatButton = document.querySelector(config.widget.newChatButton);
518
- if (newChatButton) {
519
- newChatButton.addEventListener('click', function () {
520
- app.resetSession();
521
- app.showChatScreen();
522
- });
523
- }
524
- }
525
- }
526
- function resolveSessionId(app) {
527
- if (!app || !app.widgetIsInitialized) {
528
- return null;
529
- }
530
- return localStorage.getItem(app.chatWidgetStateSession);
531
- }
532
- function onSessionInitialized(app, sessionId) {
533
- if (app && app.widgetIsInitialized) {
534
- localStorage.setItem(app.chatWidgetStateSession, sessionId);
535
- }
536
- }
537
- function onSessionReset(app) {
538
- if (app && app.widgetIsInitialized) {
539
- localStorage.removeItem(app.chatWidgetStateSession);
540
- app.clearStoredWidgetDocuments();
541
- }
542
- }
543
- function onDocumentsChanged(app, documents) {
544
- if (app && app.widgetIsInitialized) {
545
- app.saveStoredWidgetDocuments(documents);
546
- }
547
- }
548
- function handleReceiveError(app) {
549
- if (app && app.widgetIsInitialized && !app.isSessionStarted && !app._attemptedSessionRecovery) {
550
- app._attemptedSessionRecovery = true;
551
- localStorage.removeItem(app.chatWidgetStateSession);
552
- app.clearStoredWidgetDocuments();
553
- app.startNewSession();
554
- }
555
- }
556
- function dispose(app) {
557
- if (!app) {
558
- return;
559
- }
560
- if (app.widgetResizeObserver) {
561
- app.widgetResizeObserver.disconnect();
562
- app.widgetResizeObserver = null;
563
- }
564
- if (app.widgetViewportResizeHandler) {
565
- window.removeEventListener('resize', app.widgetViewportResizeHandler);
566
- app.widgetViewportResizeHandler = null;
567
- }
568
- }
569
- return {
570
- attach: attach,
571
- initialize: initialize,
572
- onMounted: initialize,
573
- resolveSessionId: resolveSessionId,
574
- onSessionInitialized: onSessionInitialized,
575
- onSessionReset: onSessionReset,
576
- onDocumentsChanged: onDocumentsChanged,
577
- handleReceiveError: handleReceiveError,
578
- dispose: dispose,
579
- onBeforeUnmount: dispose
580
- };
581
- }();
582
- window.coreAIChatWidgetManager = window.coreAIChatWidgetManager || function () {
583
- function getAttributeValue(element, attributeName) {
584
- var value = element.getAttribute(attributeName);
585
- return value === null || value === '' ? null : value;
586
- }
587
- function parseBooleanAttributeValue(value) {
588
- if (value === null || value === undefined) {
589
- return null;
590
- }
591
- if (typeof value === 'boolean') {
592
- return value;
593
- }
594
- var normalized = String(value).trim().toLowerCase();
595
- if (normalized === 'true') {
596
- return true;
597
- }
598
- if (normalized === 'false') {
599
- return false;
600
- }
601
- return null;
602
- }
603
- function parseJsonAttribute(element, attributeName, description) {
604
- var rawValue = getAttributeValue(element, attributeName);
605
- if (!rawValue) {
606
- return null;
607
- }
608
- try {
609
- return JSON.parse(rawValue);
610
- } catch (error) {
611
- console.error('Failed to parse ' + description + ' JSON.', error);
612
- return null;
613
- }
614
- }
615
- function buildOptionsFromDataAttributes(element) {
616
- var widgetContainerSelector = getAttributeValue(element, 'data-coreai-chat-widget-container-selector');
617
- if (!widgetContainerSelector) {
618
- return null;
619
- }
620
- var chatConfig = null;
621
- if (window.coreAIChatManager && typeof window.coreAIChatManager.buildConfigFromElement === 'function') {
622
- chatConfig = window.coreAIChatManager.buildConfigFromElement(element, {
623
- allowWidgetHost: true
624
- });
625
- }
626
- var profileId = getAttributeValue(element, 'data-coreai-chat-widget-profile-id');
627
- var profileName = getAttributeValue(element, 'data-coreai-chat-widget-profile-name');
628
- var profileDisplayText = getAttributeValue(element, 'data-coreai-chat-widget-profile-display-text');
629
- var profileWelcomeMessage = getAttributeValue(element, 'data-coreai-chat-widget-profile-welcome-message');
630
- var profileInitialPrompt = getAttributeValue(element, 'data-coreai-chat-widget-profile-initial-prompt');
631
- var profileChatMode = getAttributeValue(element, 'data-coreai-chat-widget-profile-chat-mode');
632
- var profileTtsVoiceName = getAttributeValue(element, 'data-coreai-chat-widget-profile-tts-voice-name');
633
- var profileRealtimeVoiceName = getAttributeValue(element, 'data-coreai-chat-widget-profile-realtime-voice-name');
634
- var profileMetricsEnabled = parseBooleanAttributeValue(getAttributeValue(element, 'data-coreai-chat-widget-profile-enable-session-metrics'));
635
- var profileSessionDocuments = parseBooleanAttributeValue(getAttributeValue(element, 'data-coreai-chat-widget-profile-allow-session-documents'));
636
- var profile = null;
637
- if (profileId || profileName || profileDisplayText || profileWelcomeMessage || profileChatMode || profileTtsVoiceName || profileRealtimeVoiceName || profileMetricsEnabled !== null || profileSessionDocuments !== null) {
638
- profile = {
639
- id: profileId,
640
- name: profileName,
641
- displayText: profileDisplayText,
642
- welcomeMessage: profileWelcomeMessage,
643
- initialPrompt: profileInitialPrompt,
644
- chatMode: profileChatMode,
645
- ttsVoiceName: profileTtsVoiceName,
646
- realtimeVoiceName: profileRealtimeVoiceName,
647
- enableSessionMetrics: profileMetricsEnabled === true,
648
- allowSessionDocuments: profileSessionDocuments === true
649
- };
650
- }
651
- var enableDragging = parseBooleanAttributeValue(getAttributeValue(element, 'data-coreai-chat-widget-enable-dragging'));
652
- var enableResizing = parseBooleanAttributeValue(getAttributeValue(element, 'data-coreai-chat-widget-enable-resizing'));
653
- var persistLayout = parseBooleanAttributeValue(getAttributeValue(element, 'data-coreai-chat-widget-persist-layout'));
654
- return {
655
- shellSelector: getAttributeValue(element, 'data-coreai-chat-widget-shell-selector'),
656
- widgetContainerSelector: widgetContainerSelector,
657
- toggleButtonSelector: getAttributeValue(element, 'data-coreai-chat-widget-toggle-button-selector'),
658
- closeButtonSelector: getAttributeValue(element, 'data-coreai-chat-widget-close-button-selector'),
659
- inputSelector: getAttributeValue(element, 'data-coreai-chat-widget-input-selector'),
660
- messageTemplateSelector: getAttributeValue(element, 'data-coreai-chat-widget-message-template-selector'),
661
- indicatorTemplateSelector: getAttributeValue(element, 'data-coreai-chat-widget-indicator-template-selector'),
662
- profile: profile,
663
- widgetStateName: getAttributeValue(element, 'data-coreai-chat-widget-state-name'),
664
- openStateValue: getAttributeValue(element, 'data-coreai-chat-widget-open-state-value'),
665
- closedStateValue: getAttributeValue(element, 'data-coreai-chat-widget-closed-state-value'),
666
- openIconHtml: getAttributeValue(element, 'data-coreai-chat-widget-open-icon-html'),
667
- closedIconHtml: getAttributeValue(element, 'data-coreai-chat-widget-closed-icon-html'),
668
- chatConfig: chatConfig ? Object.assign({}, chatConfig, {
669
- initialPrompt: profileInitialPrompt || chatConfig.initialPrompt,
670
- realtimeVoiceName: profileRealtimeVoiceName || chatConfig.realtimeVoiceName,
671
- widget: Object.assign({}, chatConfig.widget || {}, {
672
- chatWidgetContainer: widgetContainerSelector,
673
- chatWidgetStateName: getAttributeValue(element, 'data-coreai-chat-widget-state-name'),
674
- newChatButton: getAttributeValue(element, 'data-coreai-chat-widget-new-chat-button-selector'),
675
- toggleButtonSelector: getAttributeValue(element, 'data-coreai-chat-widget-toggle-button-selector'),
676
- resetSizeButtonSelector: getAttributeValue(element, 'data-coreai-chat-widget-reset-size-button-selector'),
677
- dragHandleSelector: getAttributeValue(element, 'data-coreai-chat-widget-drag-handle-selector'),
678
- enableDragging: enableDragging === null ? undefined : enableDragging,
679
- enableResizing: enableResizing === null ? undefined : enableResizing,
680
- persistLayout: persistLayout === null ? undefined : persistLayout
681
- })
682
- }) : null
683
- };
684
- }
685
- function initialize(options) {
686
- if (!options) {
687
- return null;
688
- }
689
- var chatConfig = options.chatConfig;
690
- if (!chatConfig && options.configElementSelector) {
691
- var configElement = document.querySelector(options.configElementSelector);
692
- chatConfig = configElement ? parseJsonAttribute(configElement, 'data-config', 'AI chat widget config') : null;
693
- }
694
- if (!chatConfig) {
695
- return null;
696
- }
697
- if (options.messageTemplateSelector) {
698
- var messageTemplate = document.querySelector(options.messageTemplateSelector);
699
- if (messageTemplate) {
700
- chatConfig.messageTemplate = messageTemplate.innerHTML;
701
- }
702
- }
703
- if (options.indicatorTemplateSelector) {
704
- var indicatorTemplate = document.querySelector(options.indicatorTemplateSelector);
705
- if (indicatorTemplate) {
706
- chatConfig.indicatorTemplate = indicatorTemplate.innerHTML;
707
- }
708
- }
709
- var shell = document.querySelector(options.shellSelector);
710
- var widgetContainer = document.querySelector(options.widgetContainerSelector);
711
- var toggleButton = document.querySelector(options.toggleButtonSelector);
712
- var closeButton = document.querySelector(options.closeButtonSelector);
713
- var profileInput = options.inputSelector ? document.querySelector(options.inputSelector) : null;
714
- var widgetStateName = options.widgetStateName || chatConfig.widget.chatWidgetStateName;
715
- var widgetOpenStateKey = options.widgetOpenStateKey || widgetStateName + 'Open';
716
- var widgetLayoutKey = widgetStateName + 'Layout';
717
- var openStateValue = options.openStateValue || 'open';
718
- var closedStateValue = options.closedStateValue || 'closed';
719
- if (profileInput && options.profile && options.profile.id) {
720
- profileInput.setAttribute('data-profile-id', options.profile.id);
721
- }
722
- try {
723
- var storedLayout = localStorage.getItem(widgetLayoutKey);
724
- var layout = storedLayout ? JSON.parse(storedLayout) || {} : {};
725
- var position = layout.position || {};
726
- var panelLayout = layout.panel || {};
727
- var toggleLeft = Number(position.left);
728
- var toggleTop = Number(position.top);
729
- var isOpen = localStorage.getItem(widgetOpenStateKey) === openStateValue;
730
- var viewportPadding = 8;
731
- if (shell) {
732
- shell.dataset.deferReveal = isOpen ? 'true' : 'false';
733
- shell.dataset.initialOpen = isOpen ? 'true' : 'false';
734
- }
735
- if (widgetContainer && toggleButton) {
736
- var toggleSize = 52;
737
- var panelWidth = Number.isFinite(Number(panelLayout.width)) ? Number(panelLayout.width) : Math.round(parseFloat(window.getComputedStyle(widgetContainer).width) || 380);
738
- var panelHeight = Number.isFinite(Number(panelLayout.height)) ? Number(panelLayout.height) : Math.round(parseFloat(window.getComputedStyle(widgetContainer).height) || 520);
739
- var maxLeft = Math.max(viewportPadding, window.innerWidth - toggleSize - viewportPadding);
740
- var maxTop = Math.max(viewportPadding, window.innerHeight - toggleSize - viewportPadding);
741
- if (Number.isFinite(Number(position.leftRatio))) {
742
- toggleLeft = viewportPadding + Math.max(0, maxLeft - viewportPadding) * Number(position.leftRatio);
743
- }
744
- if (Number.isFinite(Number(position.topRatio))) {
745
- toggleTop = viewportPadding + Math.max(0, maxTop - viewportPadding) * Number(position.topRatio);
746
- }
747
- if (Number.isFinite(toggleLeft) && Number.isFinite(toggleTop)) {
748
- toggleLeft = Math.round(clampWidgetValue(toggleLeft, viewportPadding, maxLeft));
749
- toggleTop = Math.round(clampWidgetValue(toggleTop, viewportPadding, maxTop));
750
- }
751
- if (Number.isFinite(toggleLeft) && Number.isFinite(toggleTop)) {
752
- toggleButton.style.left = toggleLeft + 'px';
753
- toggleButton.style.top = toggleTop + 'px';
754
- toggleButton.style.right = 'auto';
755
- toggleButton.style.bottom = 'auto';
756
- var toggleGap = 8;
757
- widgetContainer.style.left = toggleLeft + toggleSize - panelWidth + 'px';
758
- widgetContainer.style.top = toggleTop - panelHeight - toggleGap + 'px';
759
- widgetContainer.style.right = 'auto';
760
- widgetContainer.style.bottom = 'auto';
761
- }
762
- }
763
- if (widgetContainer) {
764
- widgetContainer.classList.remove('open');
765
- widgetContainer.style.display = 'none';
766
- widgetContainer.setAttribute('aria-hidden', 'true');
767
- }
768
- if (toggleButton) {
769
- toggleButton.innerHTML = isOpen ? options.openIconHtml : options.closedIconHtml;
770
- }
771
- } catch (error) {
772
- console.warn('Failed to restore widget layout before initialization.', error);
773
- } finally {
774
- if (shell && shell.dataset.deferReveal !== 'true') {
775
- shell.style.visibility = '';
776
- }
777
- }
778
- var widgetApp = window.coreAIChatManager.initialize(chatConfig);
779
- if (options.applySessionProfilePatch !== false && window.crestAppsAIChat && window.crestAppsAIChat.patchSessionProfileSync) {
780
- window.crestAppsAIChat.patchSessionProfileSync(widgetApp);
781
- }
782
- function revealShell() {
783
- if (!shell) {
784
- return;
785
- }
786
- shell.style.visibility = '';
787
- shell.dataset.deferReveal = 'false';
788
- }
789
- function syncOpenPanelPosition() {
790
- if (!widgetApp || typeof widgetApp.syncPanelPositionToToggle !== 'function') {
791
- return;
792
- }
793
- widgetApp.syncPanelPositionToToggle(false);
794
- }
795
- function updateTtsPlaybackButtons() {
796
- if (!widgetContainer) {
797
- return;
798
- }
799
- var buttons = widgetContainer.querySelectorAll('[data-tts-message-index]');
800
- buttons.forEach(function (button) {
801
- var buttonIndex = Number(button.getAttribute('data-tts-message-index'));
802
- var isPlaying = buttonIndex === widgetApp.ttsPlayingMessageIndex;
803
- button.classList.toggle('tts-playing', isPlaying);
804
- button.setAttribute('title', isPlaying ? 'Pause audio' : 'Read aloud');
805
- });
806
- }
807
- function queueTtsPlaybackButtonUpdate() {
808
- requestAnimationFrame(updateTtsPlaybackButtons);
809
- }
810
- function revealShellWhenStable(initialOpen) {
811
- if (!shell || shell.dataset.deferReveal !== 'true' || !initialOpen) {
812
- revealShell();
813
- return;
814
- }
815
- var hasRevealed = false;
816
- var resizeObserver = null;
817
- function finalizeReveal() {
818
- if (hasRevealed) {
819
- return;
820
- }
821
- hasRevealed = true;
822
- if (resizeObserver) {
823
- resizeObserver.disconnect();
824
- }
825
- requestAnimationFrame(function () {
826
- syncOpenPanelPosition();
827
- requestAnimationFrame(function () {
828
- revealShell();
829
- });
830
- });
831
- }
832
- if (typeof ResizeObserver === 'undefined') {
833
- window.setTimeout(finalizeReveal, 0);
834
- return;
835
- }
836
- var lastWidth = 0;
837
- var lastHeight = 0;
838
- var stablePasses = 0;
839
- resizeObserver = new ResizeObserver(function (entries) {
840
- var entry = entries && entries[0];
841
- if (!entry) {
842
- return;
843
- }
844
- var width = Math.round(entry.contentRect.width);
845
- var height = Math.round(entry.contentRect.height);
846
- if (!width || !height) {
847
- return;
848
- }
849
- syncOpenPanelPosition();
850
- if (width === lastWidth && height === lastHeight) {
851
- stablePasses++;
852
- } else {
853
- lastWidth = width;
854
- lastHeight = height;
855
- stablePasses = 0;
856
- }
857
- if (stablePasses >= 1) {
858
- finalizeReveal();
859
- }
860
- });
861
- resizeObserver.observe(widgetContainer);
862
- window.setTimeout(finalizeReveal, 150);
863
- }
864
- function setWidgetOpen(isOpen) {
865
- if (widgetApp) {
866
- var isCurrentlyOpen = widgetContainer.classList.contains('open');
867
- if (isOpen && !isCurrentlyOpen && typeof widgetApp.syncPanelPositionToToggle === 'function') {
868
- widgetApp.syncPanelPositionToToggle();
869
- } else if (!isOpen && isCurrentlyOpen && typeof widgetApp.syncTogglePositionToPanel === 'function') {
870
- widgetApp.syncTogglePositionToPanel();
871
- }
872
- }
873
- widgetContainer.classList.toggle('open', isOpen);
874
- widgetContainer.style.display = isOpen ? 'flex' : 'none';
875
- widgetContainer.setAttribute('aria-hidden', isOpen ? 'false' : 'true');
876
- toggleButton.innerHTML = isOpen ? options.openIconHtml : options.closedIconHtml;
877
- localStorage.setItem(widgetOpenStateKey, isOpen ? openStateValue : closedStateValue);
878
- if (isOpen && typeof window.renderPendingCharts === 'function') {
879
- setTimeout(function () {
880
- window.renderPendingCharts();
881
- }, 100);
882
- }
883
- }
884
- var initialOpen = shell && shell.dataset.initialOpen === 'true';
885
- setWidgetOpen(initialOpen);
886
- revealShellWhenStable(initialOpen);
887
- if (widgetApp && typeof widgetApp.$watch === 'function') {
888
- widgetApp.$watch('ttsPlayingMessageIndex', function () {
889
- queueTtsPlaybackButtonUpdate();
890
- });
891
- }
892
- queueTtsPlaybackButtonUpdate();
893
- if (toggleButton) {
894
- toggleButton.addEventListener('click', function () {
895
- if (toggleButton.dataset.dragSuppressClick === 'true') {
896
- toggleButton.dataset.dragSuppressClick = 'false';
897
- return;
898
- }
899
- setWidgetOpen(!widgetContainer.classList.contains('open'));
900
- });
901
- }
902
- if (closeButton) {
903
- closeButton.addEventListener('click', function () {
904
- setWidgetOpen(false);
905
- });
906
- }
907
- return widgetApp;
908
- }
909
- function initializeFromElement(element) {
910
- if (!element || element.dataset.coreAiChatWidgetInitialized === 'true') {
911
- return element ? element.__coreAIChatWidgetApp || null : null;
912
- }
913
- var jsonOptions = parseJsonAttribute(element, 'data-coreai-chat-widget-config', 'CoreAI chat widget config');
914
- var attributeOptions = buildOptionsFromDataAttributes(element);
915
- if (!jsonOptions && !attributeOptions) {
916
- return null;
917
- }
918
- var options = Object.assign({}, jsonOptions || {}, attributeOptions || {});
919
- var widgetApp = initialize(options);
920
- if (!widgetApp) {
921
- return null;
922
- }
923
- element.dataset.coreAiChatWidgetInitialized = 'true';
924
- element.__coreAIChatWidgetApp = widgetApp;
925
- return widgetApp;
926
- }
927
- function scanForAutoInitialization(root) {
928
- if (!root || typeof root.querySelectorAll !== 'function') {
929
- return;
930
- }
931
- if (typeof root.matches === 'function' && root.matches('[data-coreai-chat-widget-config],[data-coreai-chat-widget-container-selector]')) {
932
- initializeFromElement(root);
933
- }
934
- root.querySelectorAll('[data-coreai-chat-widget-config],[data-coreai-chat-widget-container-selector]').forEach(initializeFromElement);
935
- }
936
- function startAutoInitialization() {
937
- scanForAutoInitialization(document);
938
- if (typeof MutationObserver === 'undefined') {
939
- return;
940
- }
941
- var observer = new MutationObserver(function (mutations) {
942
- mutations.forEach(function (mutation) {
943
- mutation.addedNodes.forEach(function (node) {
944
- if (node && node.nodeType === 1) {
945
- scanForAutoInitialization(node);
946
- }
947
- });
948
- });
949
- });
950
- observer.observe(document.body, {
951
- childList: true,
952
- subtree: true
953
- });
954
- }
955
- if (document.readyState === 'loading') {
956
- document.addEventListener('DOMContentLoaded', startAutoInitialization, {
957
- once: true
958
- });
959
- } else {
960
- startAutoInitialization();
961
- }
962
- return {
963
- initialize: initialize,
964
- initializeFromElement: initializeFromElement
965
- };
966
- }();
5
+ (function() {
6
+ //#region src/Resources/CrestApps.AI.Resources/Assets/js/ai-chat-widget.js
7
+ function clampWidgetValue(value, min, max) {
8
+ return Math.min(Math.max(value, min), max);
9
+ }
10
+ window.coreAIChatWidgetBehavior = window.coreAIChatWidgetBehavior || function() {
11
+ function parsePixelValue(value) {
12
+ if (typeof value === "number" && Number.isFinite(value)) return value;
13
+ if (typeof value !== "string") return null;
14
+ var parsed = parseFloat(value);
15
+ return Number.isFinite(parsed) ? parsed : null;
16
+ }
17
+ function attach(app, config) {
18
+ if (!app || app.__aiChatWidgetAttached) return;
19
+ app.__aiChatWidgetAttached = true;
20
+ app.chatWidgetStateName = null;
21
+ app.chatWidgetStateSession = null;
22
+ app.chatWidgetStateDocuments = null;
23
+ app.chatWidgetLayoutKey = null;
24
+ app.chatHistorySection = null;
25
+ app.widgetContainerElement = null;
26
+ app.widgetToggleButtonElement = null;
27
+ app.widgetResetSizeButtonElement = null;
28
+ app.widgetResizeObserver = null;
29
+ app.widgetViewportResizeHandler = null;
30
+ app.widgetDefaultWidth = null;
31
+ app.widgetDefaultHeight = null;
32
+ app.widgetIsInitialized = false;
33
+ app.getStoredWidgetLayout = function() {
34
+ if (!this.chatWidgetLayoutKey || !config.widget.persistLayout) return {};
35
+ try {
36
+ var storedLayout = localStorage.getItem(this.chatWidgetLayoutKey);
37
+ return storedLayout ? JSON.parse(storedLayout) || {} : {};
38
+ } catch (error) {
39
+ console.warn("Failed to read widget layout state.", error);
40
+ return {};
41
+ }
42
+ };
43
+ app.saveStoredWidgetLayout = function(partialLayout) {
44
+ if (!this.chatWidgetLayoutKey || !config.widget.persistLayout) return;
45
+ var currentLayout = this.getStoredWidgetLayout();
46
+ var nextLayout = Object.assign({}, currentLayout);
47
+ if (partialLayout.panel !== void 0) nextLayout.panel = Object.assign({}, currentLayout.panel || {}, partialLayout.panel || {});
48
+ if (partialLayout.position !== void 0) nextLayout.position = Object.assign({}, currentLayout.position || {}, partialLayout.position || {});
49
+ localStorage.setItem(this.chatWidgetLayoutKey, JSON.stringify(nextLayout));
50
+ };
51
+ app.getElementSize = function(element, fallbackWidth, fallbackHeight) {
52
+ if (!element) return {
53
+ width: fallbackWidth || 0,
54
+ height: fallbackHeight || 0
55
+ };
56
+ var rect = element.getBoundingClientRect();
57
+ var computed = window.getComputedStyle(element);
58
+ return {
59
+ width: Math.round(rect.width || parsePixelValue(computed.width) || fallbackWidth || 0),
60
+ height: Math.round(rect.height || parsePixelValue(computed.height) || fallbackHeight || 0)
61
+ };
62
+ };
63
+ app.getElementPosition = function(element) {
64
+ if (!element) return {
65
+ left: 0,
66
+ top: 0
67
+ };
68
+ var rect = element.getBoundingClientRect();
69
+ var computed = window.getComputedStyle(element);
70
+ var inlineLeft = parsePixelValue(element.style.left);
71
+ var inlineTop = parsePixelValue(element.style.top);
72
+ var computedLeft = parsePixelValue(computed.left);
73
+ var computedTop = parsePixelValue(computed.top);
74
+ var isHidden = computed.display === "none";
75
+ return {
76
+ left: Math.round(isHidden && Number.isFinite(inlineLeft) ? inlineLeft : rect.left || inlineLeft || computedLeft || 0),
77
+ top: Math.round(isHidden && Number.isFinite(inlineTop) ? inlineTop : rect.top || inlineTop || computedTop || 0)
78
+ };
79
+ };
80
+ app.getAvailableWidgetPositionRange = function(width, height) {
81
+ var viewportPadding = 8;
82
+ return {
83
+ padding: viewportPadding,
84
+ maxLeft: Math.max(viewportPadding, window.innerWidth - width - viewportPadding),
85
+ maxTop: Math.max(viewportPadding, window.innerHeight - height - viewportPadding)
86
+ };
87
+ };
88
+ app.createStoredPosition = function(left, top, width, height) {
89
+ var range = this.getAvailableWidgetPositionRange(width, height);
90
+ var leftSpan = Math.max(0, range.maxLeft - range.padding);
91
+ var topSpan = Math.max(0, range.maxTop - range.padding);
92
+ return {
93
+ left: Math.round(left),
94
+ top: Math.round(top),
95
+ leftRatio: leftSpan === 0 ? 0 : (left - range.padding) / leftSpan,
96
+ topRatio: topSpan === 0 ? 0 : (top - range.padding) / topSpan
97
+ };
98
+ };
99
+ app.resolveStoredPosition = function(storedPosition, width, height) {
100
+ if (!storedPosition) return null;
101
+ var range = this.getAvailableWidgetPositionRange(width, height);
102
+ var left = Number(storedPosition.left);
103
+ var top = Number(storedPosition.top);
104
+ var leftRatio = Number(storedPosition.leftRatio);
105
+ var topRatio = Number(storedPosition.topRatio);
106
+ if (Number.isFinite(leftRatio)) left = range.padding + Math.max(0, range.maxLeft - range.padding) * leftRatio;
107
+ if (Number.isFinite(topRatio)) top = range.padding + Math.max(0, range.maxTop - range.padding) * topRatio;
108
+ if (!Number.isFinite(left) || !Number.isFinite(top)) return null;
109
+ return this.clampWidgetPosition(left, top, width, height);
110
+ };
111
+ app.clampWidgetPosition = function(left, top, width, height) {
112
+ var range = this.getAvailableWidgetPositionRange(width, height);
113
+ return {
114
+ left: Math.round(clampWidgetValue(left, range.padding, range.maxLeft)),
115
+ top: Math.round(clampWidgetValue(top, range.padding, range.maxTop))
116
+ };
117
+ };
118
+ app.getStoredWidgetDocuments = function() {
119
+ this.clearStoredWidgetDocuments();
120
+ return [];
121
+ };
122
+ app.saveStoredWidgetDocuments = function() {
123
+ this.clearStoredWidgetDocuments();
124
+ };
125
+ app.clearStoredWidgetDocuments = function() {
126
+ if (!this.chatWidgetStateDocuments) return;
127
+ localStorage.removeItem(this.chatWidgetStateDocuments);
128
+ };
129
+ app.applyAbsolutePosition = function(element, left, top) {
130
+ if (!element) return;
131
+ element.style.left = left + "px";
132
+ element.style.top = top + "px";
133
+ element.style.right = "auto";
134
+ element.style.bottom = "auto";
135
+ };
136
+ app.getWidgetDefaultSize = function() {
137
+ return {
138
+ width: this.widgetDefaultWidth || parsePixelValue(config.widget.defaultWidth) || 380,
139
+ height: this.widgetDefaultHeight || parsePixelValue(config.widget.defaultHeight) || 520
140
+ };
141
+ };
142
+ app.getWidgetToggleGap = function() {
143
+ return 8;
144
+ };
145
+ app.isWidgetPanelVisible = function() {
146
+ if (!this.widgetContainerElement) return false;
147
+ var computed = window.getComputedStyle(this.widgetContainerElement);
148
+ return this.widgetContainerElement.classList.contains("open") && computed.display !== "none";
149
+ };
150
+ app.persistWidgetPanelPosition = function() {
151
+ if (!this.widgetContainerElement || !config.widget.persistLayout) return;
152
+ var panelDefaultSize = this.getWidgetDefaultSize();
153
+ var panelPosition = this.getElementPosition(this.widgetContainerElement);
154
+ var panelSize = this.getElementSize(this.widgetContainerElement, panelDefaultSize.width, panelDefaultSize.height);
155
+ var toggleSize = this.getElementSize(this.widgetToggleButtonElement, 52, 52);
156
+ var linkedPosition = this.clampWidgetPosition(panelPosition.left + panelSize.width - toggleSize.width, panelPosition.top + panelSize.height + this.getWidgetToggleGap(), toggleSize.width, toggleSize.height);
157
+ this.saveStoredWidgetLayout({ position: this.createStoredPosition(linkedPosition.left, linkedPosition.top, toggleSize.width, toggleSize.height) });
158
+ };
159
+ app.persistWidgetTogglePosition = function() {
160
+ if (!this.widgetToggleButtonElement || !config.widget.persistLayout) return;
161
+ var togglePosition = this.getElementPosition(this.widgetToggleButtonElement);
162
+ var toggleSize = this.getElementSize(this.widgetToggleButtonElement, 52, 52);
163
+ this.saveStoredWidgetLayout({ position: this.createStoredPosition(togglePosition.left, togglePosition.top, toggleSize.width, toggleSize.height) });
164
+ };
165
+ app.persistWidgetPanelSize = function() {
166
+ if (!this.widgetContainerElement || !config.widget.enableResizing || !config.widget.persistLayout) return;
167
+ var defaultSize = this.getWidgetDefaultSize();
168
+ var currentSize = this.getElementSize(this.widgetContainerElement, defaultSize.width, defaultSize.height);
169
+ this.saveStoredWidgetLayout({ panel: {
170
+ width: Math.abs(currentSize.width - defaultSize.width) <= 1 ? null : currentSize.width,
171
+ height: Math.abs(currentSize.height - defaultSize.height) <= 1 ? null : currentSize.height
172
+ } });
173
+ };
174
+ app.syncTogglePositionToPanel = function(persistLayout) {
175
+ if (persistLayout === void 0) persistLayout = true;
176
+ if (!this.widgetContainerElement || !this.widgetToggleButtonElement) return;
177
+ var panelDefaultSize = this.getWidgetDefaultSize();
178
+ var panelSize = this.getElementSize(this.widgetContainerElement, panelDefaultSize.width, panelDefaultSize.height);
179
+ var toggleSize = this.getElementSize(this.widgetToggleButtonElement, 52, 52);
180
+ var panelPosition = this.getElementPosition(this.widgetContainerElement);
181
+ var linkedPosition = this.clampWidgetPosition(panelPosition.left + panelSize.width - toggleSize.width, panelPosition.top + panelSize.height + this.getWidgetToggleGap(), toggleSize.width, toggleSize.height);
182
+ this.applyAbsolutePosition(this.widgetToggleButtonElement, linkedPosition.left, linkedPosition.top);
183
+ if (persistLayout && config.widget.persistLayout) this.persistWidgetTogglePosition();
184
+ };
185
+ app.syncPanelPositionToToggle = function(persistLayout) {
186
+ if (persistLayout === void 0) persistLayout = true;
187
+ if (!this.widgetContainerElement || !this.widgetToggleButtonElement) return;
188
+ var panelDefaultSize = this.getWidgetDefaultSize();
189
+ var panelSize = this.getElementSize(this.widgetContainerElement, panelDefaultSize.width, panelDefaultSize.height);
190
+ var toggleSize = this.getElementSize(this.widgetToggleButtonElement, 52, 52);
191
+ var togglePosition = this.getElementPosition(this.widgetToggleButtonElement);
192
+ var linkedPosition = this.clampWidgetPosition(togglePosition.left + toggleSize.width - panelSize.width, togglePosition.top - panelSize.height - this.getWidgetToggleGap(), panelSize.width, panelSize.height);
193
+ this.applyAbsolutePosition(this.widgetContainerElement, linkedPosition.left, linkedPosition.top);
194
+ if (persistLayout && config.widget.persistLayout) this.persistWidgetPanelPosition();
195
+ };
196
+ app.updateWidgetResetSizeButtonVisibility = function() {
197
+ if (!this.widgetResetSizeButtonElement) return;
198
+ if (!config.widget.enableResizing || !this.widgetContainerElement) {
199
+ this.widgetResetSizeButtonElement.classList.add("d-none");
200
+ return;
201
+ }
202
+ var defaultSize = this.getWidgetDefaultSize();
203
+ var currentSize = this.getElementSize(this.widgetContainerElement, defaultSize.width, defaultSize.height);
204
+ var isResized = Math.abs(currentSize.width - defaultSize.width) > 1 || Math.abs(currentSize.height - defaultSize.height) > 1 || !!this.widgetContainerElement.style.width || !!this.widgetContainerElement.style.height;
205
+ this.widgetResetSizeButtonElement.classList.toggle("d-none", !isResized);
206
+ };
207
+ app.resetWidgetSize = function() {
208
+ if (!this.widgetContainerElement || !config.widget.enableResizing) return;
209
+ this.widgetContainerElement.style.removeProperty("width");
210
+ this.widgetContainerElement.style.removeProperty("height");
211
+ this.persistWidgetPanelSize();
212
+ this.constrainWidgetElementsToViewport();
213
+ this.updateWidgetResetSizeButtonVisibility();
214
+ };
215
+ app.restoreWidgetLayout = function() {
216
+ var storedLayout = this.getStoredWidgetLayout();
217
+ if (this.widgetContainerElement && config.widget.enableResizing) {
218
+ if (Number.isFinite(storedLayout.panel && storedLayout.panel.width)) this.widgetContainerElement.style.width = storedLayout.panel.width + "px";
219
+ else this.widgetContainerElement.style.removeProperty("width");
220
+ if (Number.isFinite(storedLayout.panel && storedLayout.panel.height)) this.widgetContainerElement.style.height = storedLayout.panel.height + "px";
221
+ else this.widgetContainerElement.style.removeProperty("height");
222
+ }
223
+ var toggleSize = this.getElementSize(this.widgetToggleButtonElement, 52, 52);
224
+ var storedTogglePosition = this.resolveStoredPosition(storedLayout.position, toggleSize.width, toggleSize.height);
225
+ if (this.widgetToggleButtonElement && config.widget.enableDragging && storedTogglePosition) {
226
+ this.applyAbsolutePosition(this.widgetToggleButtonElement, storedTogglePosition.left, storedTogglePosition.top);
227
+ this.syncPanelPositionToToggle(false);
228
+ } else if (this.widgetToggleButtonElement && config.widget.enableDragging && Number.isFinite(storedLayout.toggle && storedLayout.toggle.left) && Number.isFinite(storedLayout.toggle && storedLayout.toggle.top)) {
229
+ this.applyAbsolutePosition(this.widgetToggleButtonElement, storedLayout.toggle.left, storedLayout.toggle.top);
230
+ this.syncPanelPositionToToggle(false);
231
+ } else if (this.widgetContainerElement && config.widget.enableDragging && Number.isFinite(storedLayout.panel && storedLayout.panel.left) && Number.isFinite(storedLayout.panel && storedLayout.panel.top)) {
232
+ this.applyAbsolutePosition(this.widgetContainerElement, storedLayout.panel.left, storedLayout.panel.top);
233
+ this.syncTogglePositionToPanel(false);
234
+ }
235
+ this.constrainWidgetElementsToViewport();
236
+ this.updateWidgetResetSizeButtonVisibility();
237
+ };
238
+ app.attachDraggableWidgetElement = function(element, handle, layoutKey, suppressClickOnDrag) {
239
+ if (!element || !handle || !config.widget.enableDragging) return;
240
+ var activePointerId = null;
241
+ var startX = 0;
242
+ var startY = 0;
243
+ var startLeft = 0;
244
+ var startTop = 0;
245
+ var dragged = false;
246
+ var dragThreshold = 4;
247
+ var interactiveSelector = "button, a, input, textarea, select, option, label";
248
+ var self = this;
249
+ function stopDragging(event) {
250
+ if (activePointerId === null) return;
251
+ if (event && event.pointerId !== void 0 && event.pointerId !== activePointerId) return;
252
+ document.removeEventListener("pointermove", onPointerMove);
253
+ document.removeEventListener("pointerup", stopDragging);
254
+ document.removeEventListener("pointercancel", stopDragging);
255
+ element.classList.remove("ai-chat-widget-dragging");
256
+ activePointerId = null;
257
+ if (!dragged) return;
258
+ if (layoutKey === "panel") {
259
+ self.syncTogglePositionToPanel(false);
260
+ self.persistWidgetPanelPosition();
261
+ } else if (layoutKey === "toggle") {
262
+ self.syncPanelPositionToToggle(false);
263
+ self.persistWidgetTogglePosition();
264
+ }
265
+ if (config.widget.persistLayout) {
266
+ self.persistWidgetPanelPosition();
267
+ self.persistWidgetTogglePosition();
268
+ }
269
+ if (suppressClickOnDrag) {
270
+ element.dataset.dragSuppressClick = "true";
271
+ window.setTimeout(function() {
272
+ if (element.dataset.dragSuppressClick === "true") element.dataset.dragSuppressClick = "false";
273
+ }, 0);
274
+ }
275
+ }
276
+ function onPointerMove(event) {
277
+ if (activePointerId === null || event.pointerId !== activePointerId) return;
278
+ var deltaX = event.clientX - startX;
279
+ var deltaY = event.clientY - startY;
280
+ if (!dragged && Math.hypot(deltaX, deltaY) < dragThreshold) return;
281
+ dragged = true;
282
+ var defaultSize = self.getWidgetDefaultSize();
283
+ var currentSize = self.getElementSize(element, layoutKey === "panel" ? defaultSize.width : 52, layoutKey === "panel" ? defaultSize.height : 52);
284
+ var nextPosition = self.clampWidgetPosition(startLeft + deltaX, startTop + deltaY, currentSize.width, currentSize.height);
285
+ self.applyAbsolutePosition(element, nextPosition.left, nextPosition.top);
286
+ if (layoutKey === "panel") self.syncTogglePositionToPanel(false);
287
+ else if (layoutKey === "toggle") self.syncPanelPositionToToggle(false);
288
+ }
289
+ handle.addEventListener("pointerdown", function(event) {
290
+ if (event.button !== 0) return;
291
+ if (layoutKey === "panel" && event.target.closest(interactiveSelector)) return;
292
+ var rect = element.getBoundingClientRect();
293
+ startX = event.clientX;
294
+ startY = event.clientY;
295
+ startLeft = rect.left;
296
+ startTop = rect.top;
297
+ dragged = false;
298
+ activePointerId = event.pointerId;
299
+ element.classList.add("ai-chat-widget-dragging");
300
+ if (handle.setPointerCapture) handle.setPointerCapture(event.pointerId);
301
+ document.addEventListener("pointermove", onPointerMove);
302
+ document.addEventListener("pointerup", stopDragging);
303
+ document.addEventListener("pointercancel", stopDragging);
304
+ event.preventDefault();
305
+ });
306
+ };
307
+ app.constrainWidgetElementsToViewport = function() {
308
+ if (this.widgetContainerElement && this.widgetContainerElement.style.left && this.isWidgetPanelVisible()) {
309
+ var panelDefaultSize = this.getWidgetDefaultSize();
310
+ var panelSize = this.getElementSize(this.widgetContainerElement, panelDefaultSize.width, panelDefaultSize.height);
311
+ var panelPosition = this.getElementPosition(this.widgetContainerElement);
312
+ var clampedPanel = this.clampWidgetPosition(panelPosition.left, panelPosition.top, panelSize.width, panelSize.height);
313
+ this.applyAbsolutePosition(this.widgetContainerElement, clampedPanel.left, clampedPanel.top);
314
+ this.syncTogglePositionToPanel(false);
315
+ if (config.widget.persistLayout) {
316
+ this.persistWidgetPanelPosition();
317
+ this.persistWidgetTogglePosition();
318
+ }
319
+ } else if (this.widgetToggleButtonElement && this.widgetToggleButtonElement.style.left) {
320
+ var toggleSize = this.getElementSize(this.widgetToggleButtonElement, 52, 52);
321
+ var togglePosition = this.getElementPosition(this.widgetToggleButtonElement);
322
+ var clampedToggle = this.clampWidgetPosition(togglePosition.left, togglePosition.top, toggleSize.width, toggleSize.height);
323
+ this.applyAbsolutePosition(this.widgetToggleButtonElement, clampedToggle.left, clampedToggle.top);
324
+ this.syncPanelPositionToToggle(false);
325
+ if (config.widget.persistLayout) {
326
+ this.persistWidgetTogglePosition();
327
+ this.persistWidgetPanelPosition();
328
+ }
329
+ }
330
+ };
331
+ app.showChatScreen = function() {
332
+ if (!this.chatHistorySection) return;
333
+ this.chatHistorySection.classList.remove("show");
334
+ };
335
+ }
336
+ function initialize(app, config) {
337
+ if (!app || app.widgetIsInitialized) return;
338
+ if (!config.widget.chatWidgetContainer) {
339
+ console.error("The widget chatWidgetContainer is required.");
340
+ return;
341
+ }
342
+ if (!config.widget.chatWidgetStateName) {
343
+ console.error("The widget chatWidgetStateName is required.");
344
+ return;
345
+ }
346
+ var chatWidgetContainer = document.querySelector(config.widget.chatWidgetContainer);
347
+ if (!chatWidgetContainer) return;
348
+ if (config.widget.chatHistorySection) app.chatHistorySection = document.querySelector(config.widget.chatHistorySection);
349
+ app.chatWidgetStateName = config.widget.chatWidgetStateName;
350
+ app.chatWidgetStateSession = config.widget.chatWidgetStateName + "Session";
351
+ app.chatWidgetStateDocuments = config.widget.chatWidgetStateName + "Documents";
352
+ app.chatWidgetLayoutKey = config.widget.chatWidgetStateName + "Layout";
353
+ app.widgetContainerElement = chatWidgetContainer;
354
+ app.widgetToggleButtonElement = config.widget.toggleButtonSelector ? document.querySelector(config.widget.toggleButtonSelector) : null;
355
+ app.widgetResetSizeButtonElement = config.widget.resetSizeButtonSelector ? document.querySelector(config.widget.resetSizeButtonSelector) : null;
356
+ app.widgetDefaultWidth = parsePixelValue(config.widget.defaultWidth) || parsePixelValue(window.getComputedStyle(chatWidgetContainer).width) || 380;
357
+ app.widgetDefaultHeight = parsePixelValue(config.widget.defaultHeight) || parsePixelValue(window.getComputedStyle(chatWidgetContainer).height) || 520;
358
+ app.widgetIsInitialized = true;
359
+ app.clearStoredWidgetDocuments();
360
+ app.documents = [];
361
+ if (config.widget.enableResizing) {
362
+ chatWidgetContainer.classList.add("ai-chat-widget-resizable");
363
+ chatWidgetContainer.style.minWidth = (parsePixelValue(config.widget.minWidth) || 320) + "px";
364
+ chatWidgetContainer.style.minHeight = (parsePixelValue(config.widget.minHeight) || 420) + "px";
365
+ if (typeof ResizeObserver !== "undefined") {
366
+ app.widgetResizeObserver = new ResizeObserver(function() {
367
+ app.persistWidgetPanelSize();
368
+ app.constrainWidgetElementsToViewport();
369
+ app.updateWidgetResetSizeButtonVisibility();
370
+ });
371
+ app.widgetResizeObserver.observe(chatWidgetContainer);
372
+ }
373
+ }
374
+ var dragHandleSelector = config.widget.dragHandleSelector || ".ai-chat-widget-header";
375
+ var dragHandle = chatWidgetContainer.querySelector(dragHandleSelector);
376
+ if (config.widget.enableDragging && dragHandle) {
377
+ dragHandle.classList.add("ai-chat-widget-drag-handle");
378
+ chatWidgetContainer.classList.add("ai-chat-widget-draggable");
379
+ app.attachDraggableWidgetElement(chatWidgetContainer, dragHandle, "panel", false);
380
+ }
381
+ if (config.widget.enableDragging && app.widgetToggleButtonElement) {
382
+ app.widgetToggleButtonElement.classList.add("ai-chat-widget-draggable");
383
+ app.attachDraggableWidgetElement(app.widgetToggleButtonElement, app.widgetToggleButtonElement, "toggle", true);
384
+ }
385
+ if (app.widgetResetSizeButtonElement) app.widgetResetSizeButtonElement.addEventListener("click", function(event) {
386
+ event.preventDefault();
387
+ app.resetWidgetSize();
388
+ });
389
+ app.restoreWidgetLayout();
390
+ if (!app.widgetViewportResizeHandler) {
391
+ app.widgetViewportResizeHandler = function() {
392
+ if (config.widget.persistLayout) app.restoreWidgetLayout();
393
+ else app.constrainWidgetElementsToViewport();
394
+ app.updateWidgetResetSizeButtonVisibility();
395
+ };
396
+ window.addEventListener("resize", app.widgetViewportResizeHandler);
397
+ }
398
+ app.reloadCurrentSession();
399
+ if (config.widget.showHistoryButton && app.chatHistorySection) {
400
+ var showHistoryButton = document.querySelector(config.widget.showHistoryButton);
401
+ if (showHistoryButton) showHistoryButton.addEventListener("click", function() {
402
+ app.chatHistorySection.classList.toggle("show");
403
+ });
404
+ if (config.widget.closeHistoryButton) {
405
+ var closeHistoryButton = document.querySelector(config.widget.closeHistoryButton);
406
+ if (closeHistoryButton) closeHistoryButton.addEventListener("click", function() {
407
+ app.showChatScreen();
408
+ });
409
+ }
410
+ }
411
+ if (config.widget.newChatButton) {
412
+ var newChatButton = document.querySelector(config.widget.newChatButton);
413
+ if (newChatButton) newChatButton.addEventListener("click", function() {
414
+ app.resetSession();
415
+ app.showChatScreen();
416
+ });
417
+ }
418
+ }
419
+ function resolveSessionId(app) {
420
+ if (!app || !app.widgetIsInitialized) return null;
421
+ return localStorage.getItem(app.chatWidgetStateSession);
422
+ }
423
+ function onSessionInitialized(app, sessionId) {
424
+ if (app && app.widgetIsInitialized) localStorage.setItem(app.chatWidgetStateSession, sessionId);
425
+ }
426
+ function onSessionReset(app) {
427
+ if (app && app.widgetIsInitialized) {
428
+ localStorage.removeItem(app.chatWidgetStateSession);
429
+ app.clearStoredWidgetDocuments();
430
+ }
431
+ }
432
+ function onDocumentsChanged(app, documents) {
433
+ if (app && app.widgetIsInitialized) app.saveStoredWidgetDocuments(documents);
434
+ }
435
+ function handleReceiveError(app) {
436
+ if (app && app.widgetIsInitialized && !app.isSessionStarted && !app._attemptedSessionRecovery) {
437
+ app._attemptedSessionRecovery = true;
438
+ localStorage.removeItem(app.chatWidgetStateSession);
439
+ app.clearStoredWidgetDocuments();
440
+ app.startNewSession();
441
+ }
442
+ }
443
+ function dispose(app) {
444
+ if (!app) return;
445
+ if (app.widgetResizeObserver) {
446
+ app.widgetResizeObserver.disconnect();
447
+ app.widgetResizeObserver = null;
448
+ }
449
+ if (app.widgetViewportResizeHandler) {
450
+ window.removeEventListener("resize", app.widgetViewportResizeHandler);
451
+ app.widgetViewportResizeHandler = null;
452
+ }
453
+ }
454
+ return {
455
+ attach,
456
+ initialize,
457
+ onMounted: initialize,
458
+ resolveSessionId,
459
+ onSessionInitialized,
460
+ onSessionReset,
461
+ onDocumentsChanged,
462
+ handleReceiveError,
463
+ dispose,
464
+ onBeforeUnmount: dispose
465
+ };
466
+ }();
467
+ window.coreAIChatWidgetManager = window.coreAIChatWidgetManager || function() {
468
+ function getAttributeValue(element, attributeName) {
469
+ var value = element.getAttribute(attributeName);
470
+ return value === null || value === "" ? null : value;
471
+ }
472
+ function parseBooleanAttributeValue(value) {
473
+ if (value === null || value === void 0) return null;
474
+ if (typeof value === "boolean") return value;
475
+ var normalized = String(value).trim().toLowerCase();
476
+ if (normalized === "true") return true;
477
+ if (normalized === "false") return false;
478
+ return null;
479
+ }
480
+ function parseJsonAttribute(element, attributeName, description) {
481
+ var rawValue = getAttributeValue(element, attributeName);
482
+ if (!rawValue) return null;
483
+ try {
484
+ return JSON.parse(rawValue);
485
+ } catch (error) {
486
+ console.error("Failed to parse " + description + " JSON.", error);
487
+ return null;
488
+ }
489
+ }
490
+ function buildOptionsFromDataAttributes(element) {
491
+ var widgetContainerSelector = getAttributeValue(element, "data-coreai-chat-widget-container-selector");
492
+ if (!widgetContainerSelector) return null;
493
+ var chatConfig = null;
494
+ if (window.coreAIChatManager && typeof window.coreAIChatManager.buildConfigFromElement === "function") chatConfig = window.coreAIChatManager.buildConfigFromElement(element, { allowWidgetHost: true });
495
+ var profileId = getAttributeValue(element, "data-coreai-chat-widget-profile-id");
496
+ var profileName = getAttributeValue(element, "data-coreai-chat-widget-profile-name");
497
+ var profileDisplayText = getAttributeValue(element, "data-coreai-chat-widget-profile-display-text");
498
+ var profileWelcomeMessage = getAttributeValue(element, "data-coreai-chat-widget-profile-welcome-message");
499
+ var profileInitialPrompt = getAttributeValue(element, "data-coreai-chat-widget-profile-initial-prompt");
500
+ var profileChatMode = getAttributeValue(element, "data-coreai-chat-widget-profile-chat-mode");
501
+ var profileTtsVoiceName = getAttributeValue(element, "data-coreai-chat-widget-profile-tts-voice-name");
502
+ var profileRealtimeVoiceName = getAttributeValue(element, "data-coreai-chat-widget-profile-realtime-voice-name");
503
+ var profileMetricsEnabled = parseBooleanAttributeValue(getAttributeValue(element, "data-coreai-chat-widget-profile-enable-session-metrics"));
504
+ var profileSessionDocuments = parseBooleanAttributeValue(getAttributeValue(element, "data-coreai-chat-widget-profile-allow-session-documents"));
505
+ var profile = null;
506
+ if (profileId || profileName || profileDisplayText || profileWelcomeMessage || profileChatMode || profileTtsVoiceName || profileRealtimeVoiceName || profileMetricsEnabled !== null || profileSessionDocuments !== null) profile = {
507
+ id: profileId,
508
+ name: profileName,
509
+ displayText: profileDisplayText,
510
+ welcomeMessage: profileWelcomeMessage,
511
+ initialPrompt: profileInitialPrompt,
512
+ chatMode: profileChatMode,
513
+ ttsVoiceName: profileTtsVoiceName,
514
+ realtimeVoiceName: profileRealtimeVoiceName,
515
+ enableSessionMetrics: profileMetricsEnabled === true,
516
+ allowSessionDocuments: profileSessionDocuments === true
517
+ };
518
+ var enableDragging = parseBooleanAttributeValue(getAttributeValue(element, "data-coreai-chat-widget-enable-dragging"));
519
+ var enableResizing = parseBooleanAttributeValue(getAttributeValue(element, "data-coreai-chat-widget-enable-resizing"));
520
+ var persistLayout = parseBooleanAttributeValue(getAttributeValue(element, "data-coreai-chat-widget-persist-layout"));
521
+ return {
522
+ shellSelector: getAttributeValue(element, "data-coreai-chat-widget-shell-selector"),
523
+ widgetContainerSelector,
524
+ toggleButtonSelector: getAttributeValue(element, "data-coreai-chat-widget-toggle-button-selector"),
525
+ closeButtonSelector: getAttributeValue(element, "data-coreai-chat-widget-close-button-selector"),
526
+ inputSelector: getAttributeValue(element, "data-coreai-chat-widget-input-selector"),
527
+ messageTemplateSelector: getAttributeValue(element, "data-coreai-chat-widget-message-template-selector"),
528
+ indicatorTemplateSelector: getAttributeValue(element, "data-coreai-chat-widget-indicator-template-selector"),
529
+ profile,
530
+ widgetStateName: getAttributeValue(element, "data-coreai-chat-widget-state-name"),
531
+ openStateValue: getAttributeValue(element, "data-coreai-chat-widget-open-state-value"),
532
+ closedStateValue: getAttributeValue(element, "data-coreai-chat-widget-closed-state-value"),
533
+ openIconHtml: getAttributeValue(element, "data-coreai-chat-widget-open-icon-html"),
534
+ closedIconHtml: getAttributeValue(element, "data-coreai-chat-widget-closed-icon-html"),
535
+ chatConfig: chatConfig ? Object.assign({}, chatConfig, {
536
+ initialPrompt: profileInitialPrompt || chatConfig.initialPrompt,
537
+ realtimeVoiceName: profileRealtimeVoiceName || chatConfig.realtimeVoiceName,
538
+ widget: Object.assign({}, chatConfig.widget || {}, {
539
+ chatWidgetContainer: widgetContainerSelector,
540
+ chatWidgetStateName: getAttributeValue(element, "data-coreai-chat-widget-state-name"),
541
+ newChatButton: getAttributeValue(element, "data-coreai-chat-widget-new-chat-button-selector"),
542
+ toggleButtonSelector: getAttributeValue(element, "data-coreai-chat-widget-toggle-button-selector"),
543
+ resetSizeButtonSelector: getAttributeValue(element, "data-coreai-chat-widget-reset-size-button-selector"),
544
+ dragHandleSelector: getAttributeValue(element, "data-coreai-chat-widget-drag-handle-selector"),
545
+ enableDragging: enableDragging === null ? void 0 : enableDragging,
546
+ enableResizing: enableResizing === null ? void 0 : enableResizing,
547
+ persistLayout: persistLayout === null ? void 0 : persistLayout
548
+ })
549
+ }) : null
550
+ };
551
+ }
552
+ function initialize(options) {
553
+ if (!options) return null;
554
+ var chatConfig = options.chatConfig;
555
+ if (!chatConfig && options.configElementSelector) {
556
+ var configElement = document.querySelector(options.configElementSelector);
557
+ chatConfig = configElement ? parseJsonAttribute(configElement, "data-config", "AI chat widget config") : null;
558
+ }
559
+ if (!chatConfig) return null;
560
+ if (options.messageTemplateSelector) {
561
+ var messageTemplate = document.querySelector(options.messageTemplateSelector);
562
+ if (messageTemplate) chatConfig.messageTemplate = messageTemplate.innerHTML;
563
+ }
564
+ if (options.indicatorTemplateSelector) {
565
+ var indicatorTemplate = document.querySelector(options.indicatorTemplateSelector);
566
+ if (indicatorTemplate) chatConfig.indicatorTemplate = indicatorTemplate.innerHTML;
567
+ }
568
+ var shell = document.querySelector(options.shellSelector);
569
+ var widgetContainer = document.querySelector(options.widgetContainerSelector);
570
+ var toggleButton = document.querySelector(options.toggleButtonSelector);
571
+ var closeButton = document.querySelector(options.closeButtonSelector);
572
+ var profileInput = options.inputSelector ? document.querySelector(options.inputSelector) : null;
573
+ var widgetStateName = options.widgetStateName || chatConfig.widget.chatWidgetStateName;
574
+ var widgetOpenStateKey = options.widgetOpenStateKey || widgetStateName + "Open";
575
+ var widgetLayoutKey = widgetStateName + "Layout";
576
+ var openStateValue = options.openStateValue || "open";
577
+ var closedStateValue = options.closedStateValue || "closed";
578
+ if (profileInput && options.profile && options.profile.id) profileInput.setAttribute("data-profile-id", options.profile.id);
579
+ try {
580
+ var storedLayout = localStorage.getItem(widgetLayoutKey);
581
+ var layout = storedLayout ? JSON.parse(storedLayout) || {} : {};
582
+ var position = layout.position || {};
583
+ var panelLayout = layout.panel || {};
584
+ var toggleLeft = Number(position.left);
585
+ var toggleTop = Number(position.top);
586
+ var isOpen = localStorage.getItem(widgetOpenStateKey) === openStateValue;
587
+ var viewportPadding = 8;
588
+ if (shell) {
589
+ shell.dataset.deferReveal = isOpen ? "true" : "false";
590
+ shell.dataset.initialOpen = isOpen ? "true" : "false";
591
+ }
592
+ if (widgetContainer && toggleButton) {
593
+ var toggleSize = 52;
594
+ var panelWidth = Number.isFinite(Number(panelLayout.width)) ? Number(panelLayout.width) : Math.round(parseFloat(window.getComputedStyle(widgetContainer).width) || 380);
595
+ var panelHeight = Number.isFinite(Number(panelLayout.height)) ? Number(panelLayout.height) : Math.round(parseFloat(window.getComputedStyle(widgetContainer).height) || 520);
596
+ var maxLeft = Math.max(viewportPadding, window.innerWidth - toggleSize - viewportPadding);
597
+ var maxTop = Math.max(viewportPadding, window.innerHeight - toggleSize - viewportPadding);
598
+ if (Number.isFinite(Number(position.leftRatio))) toggleLeft = viewportPadding + Math.max(0, maxLeft - viewportPadding) * Number(position.leftRatio);
599
+ if (Number.isFinite(Number(position.topRatio))) toggleTop = viewportPadding + Math.max(0, maxTop - viewportPadding) * Number(position.topRatio);
600
+ if (Number.isFinite(toggleLeft) && Number.isFinite(toggleTop)) {
601
+ toggleLeft = Math.round(clampWidgetValue(toggleLeft, viewportPadding, maxLeft));
602
+ toggleTop = Math.round(clampWidgetValue(toggleTop, viewportPadding, maxTop));
603
+ }
604
+ if (Number.isFinite(toggleLeft) && Number.isFinite(toggleTop)) {
605
+ toggleButton.style.left = toggleLeft + "px";
606
+ toggleButton.style.top = toggleTop + "px";
607
+ toggleButton.style.right = "auto";
608
+ toggleButton.style.bottom = "auto";
609
+ var toggleGap = 8;
610
+ widgetContainer.style.left = toggleLeft + toggleSize - panelWidth + "px";
611
+ widgetContainer.style.top = toggleTop - panelHeight - toggleGap + "px";
612
+ widgetContainer.style.right = "auto";
613
+ widgetContainer.style.bottom = "auto";
614
+ }
615
+ }
616
+ if (widgetContainer) {
617
+ widgetContainer.classList.remove("open");
618
+ widgetContainer.style.display = "none";
619
+ widgetContainer.setAttribute("aria-hidden", "true");
620
+ }
621
+ if (toggleButton) toggleButton.innerHTML = isOpen ? options.openIconHtml : options.closedIconHtml;
622
+ } catch (error) {
623
+ console.warn("Failed to restore widget layout before initialization.", error);
624
+ } finally {
625
+ if (shell && shell.dataset.deferReveal !== "true") shell.style.visibility = "";
626
+ }
627
+ var widgetApp = window.coreAIChatManager.initialize(chatConfig);
628
+ if (options.applySessionProfilePatch !== false && window.crestAppsAIChat && window.crestAppsAIChat.patchSessionProfileSync) window.crestAppsAIChat.patchSessionProfileSync(widgetApp);
629
+ function revealShell() {
630
+ if (!shell) return;
631
+ shell.style.visibility = "";
632
+ shell.dataset.deferReveal = "false";
633
+ }
634
+ function syncOpenPanelPosition() {
635
+ if (!widgetApp || typeof widgetApp.syncPanelPositionToToggle !== "function") return;
636
+ widgetApp.syncPanelPositionToToggle(false);
637
+ }
638
+ function updateTtsPlaybackButtons() {
639
+ if (!widgetContainer) return;
640
+ widgetContainer.querySelectorAll("[data-tts-message-index]").forEach(function(button) {
641
+ var isPlaying = Number(button.getAttribute("data-tts-message-index")) === widgetApp.ttsPlayingMessageIndex;
642
+ button.classList.toggle("tts-playing", isPlaying);
643
+ button.setAttribute("title", isPlaying ? "Pause audio" : "Read aloud");
644
+ });
645
+ }
646
+ function queueTtsPlaybackButtonUpdate() {
647
+ requestAnimationFrame(updateTtsPlaybackButtons);
648
+ }
649
+ function revealShellWhenStable(initialOpen) {
650
+ if (!shell || shell.dataset.deferReveal !== "true" || !initialOpen) {
651
+ revealShell();
652
+ return;
653
+ }
654
+ var hasRevealed = false;
655
+ var resizeObserver = null;
656
+ function finalizeReveal() {
657
+ if (hasRevealed) return;
658
+ hasRevealed = true;
659
+ if (resizeObserver) resizeObserver.disconnect();
660
+ requestAnimationFrame(function() {
661
+ syncOpenPanelPosition();
662
+ requestAnimationFrame(function() {
663
+ revealShell();
664
+ });
665
+ });
666
+ }
667
+ if (typeof ResizeObserver === "undefined") {
668
+ window.setTimeout(finalizeReveal, 0);
669
+ return;
670
+ }
671
+ var lastWidth = 0;
672
+ var lastHeight = 0;
673
+ var stablePasses = 0;
674
+ resizeObserver = new ResizeObserver(function(entries) {
675
+ var entry = entries && entries[0];
676
+ if (!entry) return;
677
+ var width = Math.round(entry.contentRect.width);
678
+ var height = Math.round(entry.contentRect.height);
679
+ if (!width || !height) return;
680
+ syncOpenPanelPosition();
681
+ if (width === lastWidth && height === lastHeight) stablePasses++;
682
+ else {
683
+ lastWidth = width;
684
+ lastHeight = height;
685
+ stablePasses = 0;
686
+ }
687
+ if (stablePasses >= 1) finalizeReveal();
688
+ });
689
+ resizeObserver.observe(widgetContainer);
690
+ window.setTimeout(finalizeReveal, 150);
691
+ }
692
+ function setWidgetOpen(isOpen) {
693
+ if (widgetApp) {
694
+ var isCurrentlyOpen = widgetContainer.classList.contains("open");
695
+ if (isOpen && !isCurrentlyOpen && typeof widgetApp.syncPanelPositionToToggle === "function") widgetApp.syncPanelPositionToToggle();
696
+ else if (!isOpen && isCurrentlyOpen && typeof widgetApp.syncTogglePositionToPanel === "function") widgetApp.syncTogglePositionToPanel();
697
+ }
698
+ widgetContainer.classList.toggle("open", isOpen);
699
+ widgetContainer.style.display = isOpen ? "flex" : "none";
700
+ widgetContainer.setAttribute("aria-hidden", isOpen ? "false" : "true");
701
+ toggleButton.innerHTML = isOpen ? options.openIconHtml : options.closedIconHtml;
702
+ localStorage.setItem(widgetOpenStateKey, isOpen ? openStateValue : closedStateValue);
703
+ if (isOpen && typeof window.renderPendingCharts === "function") setTimeout(function() {
704
+ window.renderPendingCharts();
705
+ }, 100);
706
+ }
707
+ var initialOpen = shell && shell.dataset.initialOpen === "true";
708
+ setWidgetOpen(initialOpen);
709
+ revealShellWhenStable(initialOpen);
710
+ if (widgetApp && typeof widgetApp.$watch === "function") widgetApp.$watch("ttsPlayingMessageIndex", function() {
711
+ queueTtsPlaybackButtonUpdate();
712
+ });
713
+ queueTtsPlaybackButtonUpdate();
714
+ if (toggleButton) toggleButton.addEventListener("click", function() {
715
+ if (toggleButton.dataset.dragSuppressClick === "true") {
716
+ toggleButton.dataset.dragSuppressClick = "false";
717
+ return;
718
+ }
719
+ setWidgetOpen(!widgetContainer.classList.contains("open"));
720
+ });
721
+ if (closeButton) closeButton.addEventListener("click", function() {
722
+ setWidgetOpen(false);
723
+ });
724
+ return widgetApp;
725
+ }
726
+ function initializeFromElement(element) {
727
+ if (!element || element.dataset.coreAiChatWidgetInitialized === "true") return element ? element.__coreAIChatWidgetApp || null : null;
728
+ var jsonOptions = parseJsonAttribute(element, "data-coreai-chat-widget-config", "CoreAI chat widget config");
729
+ var attributeOptions = buildOptionsFromDataAttributes(element);
730
+ if (!jsonOptions && !attributeOptions) return null;
731
+ var widgetApp = initialize(Object.assign({}, jsonOptions || {}, attributeOptions || {}));
732
+ if (!widgetApp) return null;
733
+ element.dataset.coreAiChatWidgetInitialized = "true";
734
+ element.__coreAIChatWidgetApp = widgetApp;
735
+ return widgetApp;
736
+ }
737
+ function scanForAutoInitialization(root) {
738
+ if (!root || typeof root.querySelectorAll !== "function") return;
739
+ if (typeof root.matches === "function" && root.matches("[data-coreai-chat-widget-config],[data-coreai-chat-widget-container-selector]")) initializeFromElement(root);
740
+ root.querySelectorAll("[data-coreai-chat-widget-config],[data-coreai-chat-widget-container-selector]").forEach(initializeFromElement);
741
+ }
742
+ function startAutoInitialization() {
743
+ scanForAutoInitialization(document);
744
+ if (typeof MutationObserver === "undefined") return;
745
+ new MutationObserver(function(mutations) {
746
+ mutations.forEach(function(mutation) {
747
+ mutation.addedNodes.forEach(function(node) {
748
+ if (node && node.nodeType === 1) scanForAutoInitialization(node);
749
+ });
750
+ });
751
+ }).observe(document.body, {
752
+ childList: true,
753
+ subtree: true
754
+ });
755
+ }
756
+ if (document.readyState === "loading") document.addEventListener("DOMContentLoaded", startAutoInitialization, { once: true });
757
+ else startAutoInitialization();
758
+ return {
759
+ initialize,
760
+ initializeFromElement
761
+ };
762
+ }();
763
+ //#endregion
764
+ })();
967
765
  //# sourceMappingURL=ai-chat-widget.js.map